package cpabe

//package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/fentec-project/gofe/abe"
	"io"
	"math/rand"

	"os"
	"strconv"
	"strings"
	"time"
)

// base
var inst = abe.NewFAME()

// 生成公私钥
//
func Cpabe_setup() (*abe.FAMEPubKey, *abe.FAMESecKey) {
	// inst := abe.NewFAME()
	pubKey, secKey, err := inst.GenerateMasterKeys()
	if err != nil {
		panic(err)
	}
	return pubKey, secKey
}

// 生成 user 私钥
func Gen_secKey(att []string, secKey *abe.FAMESecKey) *abe.FAMEAttribKeys {
	// inst := abe.NewFAME()
	// att := strings.Fields(arr)
	keys, err := inst.GenerateAttribKeys(att, secKey)
	// fmt.Println(keys)
	if err != nil {
		panic(err)
	}
	return keys
}

func Cpabe_Enc(casenumber string, policy string, pubKey *abe.FAMEPubKey) string {
	// first wirte then read
	filename := "./cpabe/dir_data/" + casenumber + ".txt"
	content, err := os.ReadFile(filename)
	msp, err := abe.BooleanToMSP(string(policy), false)
	// fmt.Println(msp, '\n')
	if err != nil {
		panic(err)
	}
	cipher, err := inst.Encrypt(string(content), msp, pubKey)
	// fmt.Println(cipher)
	if err != nil {
		panic(err)
	}
	var str string
	str = cipher.CtPrime.String()

	// 将加密后数据写入cpabe文件

	file_cpabe_Path := filename + ".cpabe"
	file_cpabe, err := os.OpenFile(file_cpabe_Path, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("文件打开失败", err)
	}
	file_cpabe.Write([]byte(str))
	file_cpabe.Close()

	// 删除初始文件
	del_origin := os.Remove(filename)
	if del_origin != nil {
		fmt.Println(del_origin)
	}
	return file_cpabe_Path

}

func Cpabe_Dec(file_cpabe_Path string, cipher *abe.FAMECipher, keys *abe.FAMEAttribKeys, pubKey *abe.FAMEPubKey) {
	msgCheck, err := inst.Decrypt(cipher, keys, pubKey)
	if err != nil {
		// panic(err)
		fmt.Println("无法解密")
	} else {
		fmt.Println("解密成功")
		//fmt.Println(msgCheck)
		// 删除cpabe文件

		del_cpabe := os.Remove(file_cpabe_Path)
		if del_cpabe != nil {
			fmt.Println(del_cpabe)
		}
		place := strings.Index(file_cpabe_Path, ".cpabe")
		file_output_path := file_cpabe_Path[:place]
		// 写入数据
		file_output, err := os.OpenFile(file_output_path, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			fmt.Println("文件打开失败", err)
		}
		//及时关闭file句柄
		defer file_output.Close()
		//写入文件时，使用带缓存的 *Writer
		write1 := bufio.NewWriter(file_output)
		write1.Write([]byte(msgCheck))
		//Flush将缓存的文件真正写入到文件中
		write1.Flush()
	}

}

// json字符串转换
var parseJsonError = errors.New("json parse error")
var toJsonError = errors.New("to json error")

func ToJson_Pub(c *abe.FAMEPubKey) (string, error) {
	//fmt.Printf("原始结构体: %v\n", c)
	if jsonStr, err := json.Marshal(c); err != nil {
		fmt.Println("Error =", err)
		return "", parseJsonError
	} else {
		return string(jsonStr), nil
	}
}

func ParseJson_Pub(a string) (*abe.FAMEPubKey, error) {
	//fmt.Printf("原始字符串: %s\n", a)
	var c *abe.FAMEPubKey
	if err := json.Unmarshal([]byte(a), &c); err != nil {
		fmt.Println("Error =", err)
		return c, parseJsonError
	}
	//fmt.Println(c)
	return c, nil
}

func ToJson_Sec(c *abe.FAMESecKey) (string, error) {
	//fmt.Printf("原始结构体: %v\n", c)
	if jsonStr, err := json.Marshal(c); err != nil {
		fmt.Println("Error =", err)
		return "", parseJsonError
	} else {
		return string(jsonStr), nil
	}
}

func ParseJson_Sec(a string) (*abe.FAMESecKey, error) {
	//fmt.Printf("原始字符串: %s\n", a)
	var c *abe.FAMESecKey
	if err := json.Unmarshal([]byte(a), &c); err != nil {
		fmt.Println("Error =", err)
		return c, parseJsonError
	}
	//fmt.Println(c)
	return c, nil
}

// 扩充功能实现

// 将Pubkey和Seckey写入文件中保存
func Write_PubSec() {
	pubkey, seckey := Cpabe_setup()
	pubkey2, seckey2 := Cpabe_setup()
	pub, _ := ToJson_Pub(pubkey)
	pub2, _ := ToJson_Pub(pubkey2)
	sec, _ := ToJson_Sec(seckey)
	sec2, _ := ToJson_Sec(seckey2)
	// pubkey1
	file, err := os.OpenFile("./cpabe/enc/pubkey_1.txt", os.O_WRONLY|os.O_CREATE, 0644)
	defer file.Close()
	file.WriteString(pub)
	if err != nil {
		fmt.Println(err)
	}
	// pubkey2
	file2, err := os.OpenFile("./cpabe/enc/pubkey_2.txt", os.O_WRONLY|os.O_CREATE, 0644)
	defer file2.Close()
	file2.WriteString(pub2)
	if err != nil {
		fmt.Println(err)
	}
	// seckey1
	file3, err := os.OpenFile("./cpabe/enc/seckey_1.txt", os.O_WRONLY|os.O_CREATE, 0644)
	defer file3.Close()
	file3.WriteString(sec)
	if err != nil {
		fmt.Println(err)
	}
	// seckey2
	file4, err := os.OpenFile("./cpabe/enc/seckey_2.txt", os.O_WRONLY|os.O_CREATE, 0644)
	defer file4.Close()
	file4.WriteString(sec2)
	if err != nil {
		fmt.Println(err)
	}
}

// 读取文件中的Pubkey或Seckey, return string
func ReadBybuffio(path string) string {
	file, err := os.Open(path)
	if err != nil {
		fmt.Printf("打开文件失败,err:%v\n", err)
		return "1"
	}
	defer file.Close() //关闭文件,为了避免文件泄露和忘记写关闭文件

	//使用buffio读取文件内容
	reader := bufio.NewReader(file) //创建新的读的对象
	var line string
	for {
		line, _ := reader.ReadString('\n')
		//fmt.Println(line)
		return line
	}
	return line
}

// 生成1到2的随机数
func Gen_rand() string {
	rand.Seed(time.Now().UnixNano())
	random := rand.Intn(2) + 1
	key_num := strconv.Itoa(random)
	return string(key_num)
}

// 通过编号获取对应编号组的秘钥
func Get_key(num string) (string, string) {
	//fmt.Println(num)
	return ReadBybuffio(fmt.Sprintf("./cpabe/enc/pubkey_%s.txt", num)), ReadBybuffio(fmt.Sprintf("./cpabe/enc/seckey_%s.txt", num))
}

//// 面向用户的随机值存入：key=用户名，value=1 | 2
// 将用户和该用户的秘钥编码存到文件中保存, input = username output=nil
// Todo: 存节点
func Write_user(user string) string {
	file, err := os.OpenFile("./cpabe/enc/user_list.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) // 追加模式写入文件
	defer file.Close()
	// 生成随机数
	key_num := Gen_rand()
	info := user + "," + key_num // 用户和编码中间逗号隔开
	fmt.Println(info)
	fmt.Fprintln(file, info)
	//file.WriteString(info)
	if err != nil {
		fmt.Println(err)
	}
	return key_num
}

// 删除字符串中的换行和空格
func Trim(src string) (dist string) {
	if len(src) == 0 {
		return
	}
	r, distR := []rune(src), []rune{}
	for i := 0; i < len(r); i++ {
		if r[i] == 10 || r[i] == 32 {
			continue
		}
		distR = append(distR, r[i])
	}
	dist = string(distR)
	return
}

// 读取用户和用户的秘钥编码文件，并匹配用户是否存在，若用户存在返回对应编码，若不存在则返回0
func Read_casenumber(casenumber string) string {
	//打开文件
	file, err := os.Open("./cpabe/enc/casenumber_list.txt")
	if err != nil {
		fmt.Printf("打开文件失败,err:%v\n", err)
		return "0"
	}
	defer file.Close() //关闭文件,为了避免文件泄露和忘记写关闭文件

	//使用buffio读取文件内容
	reader := bufio.NewReader(file) //创建新的读的对象
	for {
		line, err := reader.ReadString('\n')
		//fmt.Println(line)
		place := strings.Index(line, ",")
		casenumber_name := line[:place]
		num := line[place+1:]
		if casenumber_name == casenumber {
			fmt.Println("用户:", casenumber_name, "的秘钥编码为 ", num)
			num = Trim(num)
			//fmt.Println(len(num))
			return num
		}
		if err == io.EOF { //如果读到末尾就会进入
			return "0"
		}
	}
}

/*
加密函数，输入为casenumber，policy
*/
func CpabeMake(user string, casenumber string, policy string) string {
	// first. pk-sk init write -> file
	// 第一次使用此函数，之后注释掉
	//Write_PubSec()
	//  1.2 get user k {0|1}
	// 将用户信息保存
	fmt.Println("start enc")
	key_num := Write_user(user)
	// 1.3 string(pk) type -> abe* pk
	pub, _ := Get_key(key_num)
	pubkey, _ := ParseJson_Pub(pub)
	// seckey, _ := ParseJson_Sec(sec)

	//gamma := []string{"医生1", "病种2", "科室2", "机构1"}

	//keys := Gen_secKey(gamma, seckey)
	//policy := "((医生1 AND 病种2) OR (研究员2 AND 病种1))" // Todo:调用数据库里的policy
	// 默认在加密时casenumber.txt 文件中已存入数据
	Enpath := Cpabe_Enc(casenumber, policy, pubkey)
	// file_cpabe_Path := filename + ".cpabe"

	// Cpabe_Dec(file_cpabe_Path, cipher, keys, pubkey)
	fmt.Println("get enc path " + Enpath)
	return Enpath
}

//func main1() {
//	// 加密的目标文件
//	filename := "11.txt"
//	// 第一次使用此函数，之后注释掉
//	// Write_PubSec()
//
//	// 用户名
//	//user := "张三"
//	casenum := "72d560a776a77"
//	// 将用户信息保存
//	// Write_user(user, key_num)
//	key_num := Read_casenumber(casenum)
//	if key_num == "0" {
//		fmt.Println("该用户不存在")
//	}
//	//fmt.Println("----------------------------------------------------------------")
//
//	pub, sec := Get_key(key_num)
//	pubkey, _ := ParseJson_Pub(pub)
//	seckey, _ := ParseJson_Sec(sec)
//	//fmt.Println(*&pubkey)
//	//fmt.Println(*&seckey)
//
//	//fmt.Println(pubkey, seckey, pubkey2, seckey2)
//	gamma := []string{"医生1", "病种2", "科室2", "机构1"}
//
//	keys := Gen_secKey(gamma, seckey)
//	policy := "((医生1 AND 病种2) OR (研究员2 AND 病种1))"
//
//	cipher := Cpabe_Enc(filename, policy, pubkey)
//	file_cpabe_Path := filename + ".cpabe"
//	time.Sleep(8 * time.Second)
//	fmt.Println(file_cpabe_Path, cipher, keys, pubkey)
//	fmt.Println("成功")
//}
